package mars.io.formatters

import mars.ns.core.Network
import mars.ns.HasState
import mars.ns.core.Node
import mars.ns.core.Link
import mars.ns.core.LinkType

import java.lang.reflect.Constructor
import java.lang.reflect.Field

import scala.io.Source
import scala.util.matching.Regex
import scala.xml.PrettyPrinter
import scala.xml.Elem
import scala.xml.XML

class NetXMLFormatter extends Formatter[Network[Node]] {

    val xmlPrettyPrinter = new PrettyPrinter(800, 4)

    def format(network: Network[Node]): String = {
        var s = <network> {
            for (node: Node <- network.getNodes) yield {
                <node id={ node.id.toString } class={ node.getClass.getName }> {
                    for (link: Link <- node.getLinks) yield {
                        <link type={ link.getType.toString } linkedTo={ link.getLinkedTo.id.toString }/>
                    }
                }</node>
            }
        }</network>;

        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xmlPrettyPrinter.format(s)
    }

    def fromString(string: String): Network[Node] = {
        val root: Elem = XML.loadString(string)
        var network: Network[Node] = new Network

        var nodes: scala.xml.NodeSeq = root \ "node"
        
        nodes = nodes.sortWith((n1, n2) => (n1 \ "@id").text.toInt < (n2 \ "@id").text.toInt )

        //Synchronized is needed to be sure that no toher nodes are created because that could mess up the id sequence 
        synchronized {
        	// we have to add all the nodes before we can add the links
        	for (nnode <- nodes) {
        		//println(nnode \ "@id")
        		val nodeType: String = (nnode \ "@type").text
        		val constr: Constructor[_] = Class.forName(nodeType).getConstructor()
        		val node: Node = constr.newInstance().asInstanceOf[Node]
        				network.addNode(node)            
        	}
        }
        
        //add the links
        for (nnode <- nodes) {
            val from: Node = network.getNodeById((nnode \ "@id").text.toInt)
            
            val links: scala.xml.NodeSeq = nnode \ "link"
            for(nlink <- links) {
                val to: Node = network.getNodeById((nnode \ "@linkedTo").text.toInt)
                
                val lType: String = (nlink \ "@class").text
                val constr: Constructor[_] = Class.forName(lType).getConstructor()
                val linkType: LinkType = constr.newInstance().asInstanceOf[LinkType]
                
                network.addLink(from, to, linkType)
            }
        }

        return network
    }

    def validFormat(string: String): Boolean = {
        val schema = this.getClass().getResource("/mars/io/formatters/network.xsd")
        return XMLValidator.isValid(string, schema)
    }
}